package com.alperez.androidsmscamera.model;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.animation.ArgbEvaluator;
import android.text.TextUtils;

/**
 * This class describes sources of SMS commands and defines whether access has been allowed or not
 * @author Stas
 *
 */
public class UserAccount {
	public enum AccountType {
		/**
		 * Access by password from defined set of phones <br>
		 * Multiple instances are stored in DB
		 */
		DEFINED_USER,
		
		/**
		 * Only password is needed for the access. <br>
		 * Multiple instances are stored in DB
		 */
		ANONYMOUS_USER,
		
		/**
		 * Access by phone number (no password is needed) <br>
		 * Multiple instances are stored in DB
		 */
		ROOT_USER,
		
		/**
		 * Only 1 instance is allowed within the application. It's stored in Shared Preferences.
		 * It's implemented as - SuperRootAccount (subclass of UserAccount)
		 * Contains 1 or several super root phone numbers and a password for super root access.
		 * This password is used to access the Application and to perform any critical actions
		 * Some critical actions can be performed only from this account's phones with this password
		 */
		SUPER_ROOT_USER
	}
	
	private int accountId;
	protected AccountType accountType;
	private int accessLevel;
	private Set<String> sourcePhones = new HashSet<String>();
	private String password;
	/**
	 * If this field is NULL - no expiration for the account
	 */
	protected AccountInfo accountInfo;
	
	public UserAccount(int accountId, AccountType accType) {
		if (accType == null) {
			throw new IllegalArgumentException("valid account type must be provided");
		} else if (accType == AccountType.SUPER_ROOT_USER) {
			throw new IllegalArgumentException("You can not create UserAccount with SUPER_ROOT_USER type. Create SuperRoorAccount instead.");
		}
		this.setAccountId(accountId);
		this.accountType = accType;
		this.sourcePhones = new HashSet<String>();
	}
	
	/**
	 * Copy constructor
	 * @param acc
	 */
	public UserAccount(UserAccount acc) {
		if (acc == null) {
			throw new IllegalArgumentException("User account to be copied can not be null");
		}
		this.accountId = acc.accountId;
		this.accountType = acc.accountType;
		this.accessLevel = acc.accessLevel;
		this.sourcePhones = new HashSet<String>();
		Iterator<String> itr = acc.sourcePhones.iterator();
		while(itr.hasNext()) {
			this.sourcePhones.add(itr.next());
		}
		this.password = new String(acc.password);
		if (acc.accountInfo != null) {
			this.accountInfo = new AccountInfo(acc.accountInfo);
		}
	}
	
	
	public int getAccountId() {
		return accountId;
	}

	public void setAccountId(int accountId) {
		this.accountId = accountId;
	}

	public AccountType getAccountType() {
		return accountType;
	}

	public int getAccessLevel() {
		return accessLevel;
	}

	public void setAccessLevel(int accessLevel) {
		this.accessLevel = accessLevel;
	}

	public Set<String> getSourcePhones() {
		return sourcePhones;
	}

	public void addSourcePhone(String sourcePhone) {
		this.sourcePhones.add(sourcePhone);
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public AccountInfo getAccountInfo() {
		return accountInfo;
	}

	public void setAccountInfo(AccountInfo accountInfo) {
		this.accountInfo = accountInfo;
	}
	
	
	public JSONObject toJSONObject() throws JSONException {
		JSONObject ret = new JSONObject();
		ret.put("accountId", accountId);
		ret.put("accountType", UserAccount.accountTypeAsString(accountType));
		ret.put("accessLevel", accessLevel);
		JSONArray jPhones = new JSONArray(sourcePhones);
		ret.put("sourcePhones", jPhones);
		if (password != null) ret.put("password", password);
		if (accountInfo != null) ret.put("accountInfo", accountInfo.toJSONObject());
		return ret;
	}
	
	public String toJson() throws JSONException {
		return this.toJSONObject().toString();
	}
	
	public void fromJson(String json) throws JSONException {
		JSONObject jObj = new JSONObject(json);
		int accId = jObj.getInt("accountId");
		AccountType accType = accountTypeFromString(jObj.getString("accountType"));
		int acsLev = jObj.getInt("accessLevel");
		JSONArray jPhones = jObj.getJSONArray("sourcePhones");
		Set<String> sPhones = new HashSet<String>();
		for (int i=0; i<jPhones.length(); i++) {
			sPhones.add(jPhones.getString(i));
		}
		
		if (jObj.has("accountInfo")) {
			this.accountInfo = new AccountInfo();
			this.accountInfo.fromJson(jObj.getString("accountInfo"));
		}
		
		this.accountId = accId;
		this.accountType = accType;
		this.accessLevel = acsLev;
		this.sourcePhones = sPhones;
		this.password = jObj.optString("password", null);
	}
	
	public static String accountTypeAsString(AccountType accType) {
		switch(accType) {
		case ANONYMOUS_USER:
			return "anonymous_user";
		case DEFINED_USER:
			return "defined_user";
		case ROOT_USER:
			return "root_user";
		case SUPER_ROOT_USER:
			return "super_root_user";
		default:
			throw new IllegalArgumentException("Provided User account type is not supported");
		}
	}
	
	public static AccountType accountTypeFromString(String accStr) {
		if (TextUtils.isEmpty(accStr)) {
			throw new IllegalArgumentException("Not null and not empty string representation of AccountType must be provided");
		}
		if (accStr.equals("anonymous_user")) {
			return AccountType.ANONYMOUS_USER;
		} else if (accStr.equals("defined_user")) {
			return AccountType.DEFINED_USER;
		} else if (accStr.equals("root_user")) {
			return AccountType.ROOT_USER;
		} else if (accStr.equals("super_root_user")) {
			return AccountType.SUPER_ROOT_USER;
		}
		throw new IllegalArgumentException("Provided string representation of User account type is not supported - "+accStr);
	}
}
